﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Collections.Specialized;
using System.Data;
using System.Threading;
using System.Collections;
using System.IO;
using System.Xml;
using System.Diagnostics;
using OBiCMS.Dean.Edwards;
using System.Web.SessionState;

/*
    Copyright (C) 2010 Officebroker.com LTD.
    Neil Cadman, Alex Welding, Andrew Coombs, Jan Rygielski
    Sir Robert Peel Mill, Mill Lane, Staffordshire, Fazely, B78 3QD, England

    This file is part of OBiCMSFramework.

    OBiCMSFramework is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    OBiCMSFramework is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License (GNU AGPL)
    along with OBiCMSFramework. If not, see <http://www.gnu.org/licenses/>.
*/

namespace OBiCMSFramework
{
    [Serializable()]
    public class BasePage : IRequiresSessionState
    {
        // Private Variables
        static private Hashtable _cachedJavaScript = new Hashtable();
        static private Hashtable _cachedBasePagesHash = new Hashtable();
        static private Hashtable _cachedCssSnippetsHash = new Hashtable();
        static private List<BasePage> _cachedBasePages = new List<BasePage>();
        static private List<WidgetBase> _cachedWidgets = new List<WidgetBase>();
        static private List<CSSSnippet> _cachedCssSnippets = new List<CSSSnippet>();

        private Boolean _online = true;
        private Boolean _runThreadAgain = true;
        private DateTime _dateChanged;
        private Dictionary<String, List<CSSSnippet>> _cssSnippets;
        private DocType _docType;
        private Int32 _minRefreshTime = 60000;
        private Int32 _maxRefreshTime = 120000;
        private List<BasePageJob> _jobs = new List<BasePageJob>();
        [NonSerialized]
        private PageSettings _pageSettings;
        private String _ContentType = "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\" />";
        private String _docTypeSyntax;
        private String _favIcon;
        private String _id;
        private String _shortcutIcon;
        private String _version = "0";

        // Private Delegates
        private delegate void loadBasePageDelegate(Int32 startStall);

        // Public Enums
        public enum DocType
        {
            HTML4_0_1,
            HTML4_0_1_Trans,
            HTML4_0_1_FrameSet,
            HTML3_2,
            HTML2_0,
            HTML5,
            XHTML1_0_Strict,
            XHTML1_0_Trans,
            XHTML1_0_FrameSet,
            XML
        }

        // Internal Properties
        internal static Hashtable CachedJavaScript
        { get { return _cachedJavaScript; } }
        internal static Hashtable CachedBasePagesHash
        {
            get { return _cachedBasePagesHash; }
        }
        internal static Hashtable CachedCssSnippetsHash
        {
            get { return _cachedCssSnippetsHash; }
        }
        internal static List<BasePage> CachedBasePages
        {
            get { return _cachedBasePages; }
        }
        internal static List<WidgetBase> CachedWidgets
        { get { return _cachedWidgets; } }
        internal static List<CSSSnippet> CachedCssSnippets
        {
            get { return _cachedCssSnippets; }
        }

        internal Boolean Online
        { get { return _online; } }
        internal DateTime DateChanged
        { get { return _dateChanged; } }
        internal Dictionary<String, List<CSSSnippet>> CSSSnippets
        { get { return _cssSnippets; } }
        internal String ID
        { get { return _id; } }

        // Private Constructors
        private BasePage(String id)
        {
            this._id = id;
            this._pageSettings = new PageSettings(this);
        }

        // Internal Methods
        internal void StopAutoLoad()
        {
            _runThreadAgain = false;
        }
        internal void InitializeBasePage()
        {
            _runThreadAgain = false;
            LoadBasePage(0);
            _runThreadAgain = true;
            AutoLoad();
        }
        internal static BasePage getBasePage(String id)
        {
            lock (_cachedBasePagesHash)
            {
                if (_cachedBasePagesHash.ContainsKey(id))
                    return (BasePage)_cachedBasePagesHash[id];

                return BasePage.GenerateNewPage(id);
            }
        }
        internal static CSSSnippet GetCssSnippet(String snippetID)
        {
            lock (_cachedCssSnippetsHash)
            {
                if (_cachedCssSnippetsHash.ContainsKey(snippetID))
                    return (CSSSnippet)_cachedCssSnippetsHash[snippetID];

                if (File.Exists(StaticObjects.SavedStatesLocation + snippetID + ".csssnippet"))
                {
                    StaticObjects.DeSerialize(StaticObjects.SavedStatesLocation + snippetID + ".csssnippet");
                    return (CSSSnippet)_cachedCssSnippetsHash[snippetID];
                }

                return GenerateNewCSSSnippet(snippetID);
            }
        }
        internal static void SetXMLSettings(BasePage basePage, String XMLSettings)
        {
            List<BasePageJob> newJobs = new List<BasePageJob>();
            StringReader xml = new StringReader(XMLSettings);
            
            if (XMLSettings != "")
            {
                XmlReader pageSettings = XmlReader.Create(xml);

                while (pageSettings.Read())
                    if (pageSettings.NodeType == XmlNodeType.Element)
                        switch (pageSettings.Name.ToLower())
                        {
                            case "template":
                                newJobs.Add(basePage.AddTemplateJob(pageSettings));
                                pageSettings.Skip();
                                break;
                        }
            }

            basePage._jobs = newJobs;
        }
        internal PageContent GeneratePage(HttpContext context, Stopwatch timer, Boolean useDiagnostics)
        {
            ContextManager contextManager = new ContextManager(context);
            PageSettings pageSettings = new PageSettings(this);
            pageSettings.AppendTitle(_pageSettings.Title.ToString());
            pageSettings.AppendMetaDescription(_pageSettings.MetaDescription.ToString());
            pageSettings.AppendMetaKeywords(_pageSettings.MetaKeywords.ToString());

            if (_docType == DocType.XML)
            {
                StringBuilder xmlString = new StringBuilder(_docTypeSyntax + Environment.NewLine);
                foreach (BasePageJob job in _jobs)
                    xmlString.Append(job.Generate(context, contextManager, pageSettings, timer, useDiagnostics));

                if (pageSettings.HeaderChanges)
                    _dateChanged = DateTime.UtcNow;

                context.Response.ContentType = "text/xml";
                return new PageContent(xmlString.ToString(), _dateChanged, _online);
            }

            // Build the page.
            StringBuilder mainBody = new StringBuilder();

            // Check if the page is a postback.
            if (contextManager.GetFormValue("CSMPostBackID") != "")
            {
                String tempGuid = Guid.NewGuid().ToString();

                foreach (BasePageJob job in _jobs)
                    if (job.TestPostBack(tempGuid, contextManager.GetFormValue("CSMPostBackID").ToString(), contextManager, pageSettings, timer, useDiagnostics))
                        break;
            }

            if (useDiagnostics)
                mainBody.Append(Environment.NewLine + "<!-- Generating page jobs at " + timer.ElapsedTicks + "-->" + Environment.NewLine);

            foreach (BasePageJob job in _jobs)
                mainBody.Append(job.Generate(context, contextManager, pageSettings, timer, useDiagnostics));

            StringBuilder basepageBody = new StringBuilder();

            // Create HTML for the page.
            basepageBody.Append(_docTypeSyntax + Environment.NewLine);
            basepageBody.Append("<!-- Page Generated by OBiCMSFramework -->" + Environment.NewLine);

            if (useDiagnostics)
            {
                basepageBody.Append(Environment.NewLine + "<!-- Page ID : " + this.ID + Environment.NewLine);
                basepageBody.Append("     Version : " + this._version + Environment.NewLine);
                basepageBody.Append("     Generated at " + _dateChanged.ToString("yyyy/MM/dd HH:mm") + " yyyy/MM/dd HH:mm)-->" + Environment.NewLine);
            }

            basepageBody.Append("<head>" + Environment.NewLine);
            basepageBody.Append(_ContentType + Environment.NewLine);
            basepageBody.Append("<title>" + System.Web.HttpUtility.HtmlEncode(pageSettings.Title.ToString()) + "</title>" + Environment.NewLine);
            basepageBody.Append("<meta name=\"Keywords\" content=\"" + pageSettings.MetaKeywords + "\" />" + Environment.NewLine);
            basepageBody.Append("<meta name=\"Description\" content=\"" + pageSettings.MetaDescription + "\" />" + Environment.NewLine);
            basepageBody.Append("<link rel=\"shortcut icon\" href=\"" + _shortcutIcon + "\" />" + Environment.NewLine);
            basepageBody.Append("<link rel=\"icon\" href=\"" + _favIcon + "\" type=\"image/gif\" />" + Environment.NewLine);

            // Create a temporary dictionary to store the CSS snippet types into
            Dictionary<String, List<CSSSnippet>> newSnippets = new Dictionary<String, List<CSSSnippet>>();

            if (_cssSnippets == null)
                pageSettings.HeaderChanges = true;

            if (useDiagnostics)
                mainBody.Append(Environment.NewLine + "<!-- Css List Count : " + pageSettings.CssSnippets.Count + " " + timer.ElapsedTicks + "-->" + Environment.NewLine);

            // Only update the dictionary if the generate reports a change is needed.
            if (pageSettings.HeaderChanges)
                foreach (CSSSnippet snippet in pageSettings.CssSnippets)
                    if (!newSnippets.ContainsKey(snippet.Type.ToString()))
                        newSnippets.Add(snippet.Type.ToString(), new List<CSSSnippet>() { snippet });
                    else
                        ((List<CSSSnippet>)newSnippets[snippet.Type.ToString()]).Add(snippet);

            // use the new css snippets
            if (pageSettings.HeaderChanges)
                _cssSnippets = newSnippets;

            // Output each diffrent type of CSS snippet.
            foreach (String cssTypeName in _cssSnippets.Keys)
                basepageBody.Append(CSSSnippet.CssTypeDeclaration((CSSSnippet.CssType)Enum.Parse(typeof(CSSSnippet.CssType), cssTypeName), this.ID) + Environment.NewLine);

            if (pageSettings.HeaderChanges)
                if (pageSettings.JavaScript.Length != 0)
                {
                    if (StaticObjects.PackJavascript.Pack)
                    {
                        ECMAScriptPacker packer = new ECMAScriptPacker(StaticObjects.PackJavascript.Encoding, StaticObjects.PackJavascript.FastDecode, StaticObjects.PackJavascript.SpecialChars);
                        _cachedJavaScript[this.ID.ToLower() + ".js_javascript"] = packer.Pack(pageSettings.JavaScript.ToString());
                    }
                    else
                        _cachedJavaScript[this.ID.ToLower() + ".js_javascript"] = pageSettings.JavaScript.ToString();
                }
                if (pageSettings.JavaScriptDefered.Length != 0)
                {
                    if (StaticObjects.PackJavascript.Pack)
                    {
                        ECMAScriptPacker packer = new ECMAScriptPacker(StaticObjects.PackJavascript.Encoding, StaticObjects.PackJavascript.FastDecode, StaticObjects.PackJavascript.SpecialChars);
                        _cachedJavaScript[this.ID.ToLower() + ".js_javascriptdefered"] = packer.Pack(pageSettings.JavaScriptDefered.ToString());
                    }
                    else
                        _cachedJavaScript[this.ID.ToLower() + ".js_javascriptdefered"] = pageSettings.JavaScriptDefered.ToString();
                }

            if (pageSettings.JavaScript.Length != 0)
                basepageBody.Append("<script type=\"text/javascript\" src=\"/" + this.ID + ".js?OBiVersion=Dynamic\"></script>" + Environment.NewLine);

            if (pageSettings.JavaScriptDefered.Length != 0)
                basepageBody.Append("<script defer type=\"text/javascript\" src=\"/" + this.ID + ".js?OBiVersion=Dynamic&amp;Defered=True\"></script>" + Environment.NewLine);

            basepageBody.Append("</head>" + Environment.NewLine);
            basepageBody.Append("<body>" + Environment.NewLine);

            basepageBody.Append(mainBody);

            basepageBody.Append("</body>" + Environment.NewLine);
            basepageBody.Append("</html>" + Environment.NewLine);

            foreach (KeyValuePair<String, String> replaceItem in pageSettings.ReplaceItems)
                basepageBody.Replace(replaceItem.Key, replaceItem.Value);

            if (pageSettings.HeaderChanges)
                _dateChanged = DateTime.UtcNow;

            return new PageContent(basepageBody.ToString(), _dateChanged, _online);
        }
        
        // Public Methods

        /// <summary>
        /// This will check to see if the widget being requested already exist, if not it will create a new widget.
        /// </summary>
        /// <param name="type">The widget type</param>
        /// <param name="ID">The widget ID</param>
        /// <returns>Returns a widget that matches the type and id</returns>
        public static WidgetBase getWidgetBase(Type type, String ID)
        {
            lock (_cachedWidgets)
            {
                for (int i = 0; i < _cachedWidgets.Count; i++)
                    if (_cachedWidgets[i].ID == ID)
                        if (_cachedWidgets[i].GetType() == type)
                            return _cachedWidgets[i];

                if (File.Exists(StaticObjects.SavedStatesLocation + ID + ".widget"))
                {
                    StaticObjects.DeSerialize(StaticObjects.SavedStatesLocation + ID + ".widget");
                    return _cachedWidgets[_cachedWidgets.Count - 1];
                }

                WidgetBase newWidget = (WidgetBase)Activator.CreateInstance(type, new Object[] { ID });
                if (newWidget.ID == ID && newWidget.GetType() == type)
                {
                    newWidget.InitializeWidget();
                    _cachedWidgets.Add(newWidget);
                    return newWidget;
                }

                // todo
                return newWidget;
            }
        }

        // Private Methods
        private void AutoLoad()
        {
            loadBasePageDelegate autoLoad = new loadBasePageDelegate(LoadBasePage);
            autoLoad.BeginInvoke(StaticObjects.RandomFactory.Create().Next(_minRefreshTime, _maxRefreshTime), new AsyncCallback(AutoLoadEnd), null);
        }
        private void LoadBasePage(Int32 startStall)
        {
            DataRow dr;
            DataSet ds;
            String sql;

            if (startStall != 0)
                Thread.Sleep(startStall);

            // Load the BasePage
            sql = "select * from getBasePage('" + this.ID + "') Order by VersionOnline Desc, Version Desc";
            ds = StaticObjects.GetDataset(sql);

            if (ds.Tables.Count == 0 || ds.Tables[0].Rows.Count == 0)
                throw new OBiHTTPHandler.CMSException("BasePage Cannot Load.", new Dictionary<String, String>() {
                                                                                 {"BasePage ID", this.ID} });

            dr = ds.Tables[0].Rows[0];
                
            // Has the version changed, if so reload the pages settings
            if (_version != dr["Version"].ToString())
            {
                BasePage.SetXMLSettings(this, dr["SettingsXML"].ToString());

                // Create a new Settings class and fill it up from the settings in the database.
                PageSettings newSettings = new PageSettings(this);
                newSettings.HeaderChanges = true;
                newSettings.AppendTitle(dr["Title"].ToString());
                newSettings.AppendMetaKeywords(dr["MetaKeywords"].ToString());
                newSettings.AppendMetaDescription(dr["MetaDescription"].ToString());

                // Replace the current instances page settings.
                _pageSettings = newSettings;

                // Update the current instances local settings
                _version = dr["Version"].ToString();
                _favIcon = dr["FavIcon"].ToString();
                _shortcutIcon = dr["ShortcutIcon"].ToString();
                _online = (dr["Online"].ToString().ToLower() == "true" ? true : false);

                // Set the pages document type
                _docType = (DocType)Enum.Parse(typeof(DocType), dr["docType"].ToString());

                switch (_docType)
                {
                    case DocType.HTML4_0_1:
                        _docTypeSyntax = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" + Environment.NewLine +
                                          "<html>";
                        break;
                    case DocType.HTML4_0_1_Trans:
                        _docTypeSyntax = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">" + Environment.NewLine +
                                          "<html>";
                        break;
                    case DocType.HTML4_0_1_FrameSet:
                        _docTypeSyntax = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\" \"http://www.w3.org/TR/html4/frameset.dtd\">" + Environment.NewLine +
                                          "<html>";
                        break;
                    case DocType.HTML3_2:
                        _docTypeSyntax = "<!DOCTYPE HTML PUBLIC \" -//W3C//DTD HTML 3.2 Final//EN\">" + Environment.NewLine +
                                          "<html>";
                        break;
                    case DocType.HTML2_0:
                        _docTypeSyntax = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML//EN\">" + Environment.NewLine +
                                          "<html>";
                        break;
                    case DocType.HTML5:
                        _docTypeSyntax = "<!DOCTYPE html>" + Environment.NewLine +
                                          "<html>";
                        break;
                    case DocType.XHTML1_0_Strict:
                        _docTypeSyntax = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" + Environment.NewLine +
                                          "<html xmlns=\"http://www.w3.org/1999/xhtml\">";
                        break;
                    case DocType.XHTML1_0_Trans:
                        _docTypeSyntax = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" + Environment.NewLine +
                                          "<html xmlns=\"http://www.w3.org/1999/xhtml\">";
                        break;
                    case DocType.XHTML1_0_FrameSet:
                        _docTypeSyntax = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">" + Environment.NewLine +
                                          "<html xmlns=\"http://www.w3.org/1999/xhtml\">";
                        break;
                    case DocType.XML:
                        _docTypeSyntax = "<?xml version=\"1.0\" ?>";
                        break;
                    default:
                        _docTypeSyntax = "<!DOCTYPE html>" + Environment.NewLine +
                                          "<html>";
                        break;
                }

                // Update the changes time to now.
                _dateChanged = DateTime.UtcNow;

            }
            else
                _pageSettings.HeaderChanges = false;

            // Automaticaly run again?
            if (_runThreadAgain)
                AutoLoad();

        }
        private void AutoLoadEnd(IAsyncResult result)
        {
        }
        private BasePageJob AddWidgetJob(XmlReader reader)
        {
            String widgetName = reader.GetAttribute("name");
            WidgetBase newWidget = getWidgetBase(Type.GetType(reader.GetAttribute("type")), reader.GetAttribute("id"));
            XmlReader subnodes = reader.ReadSubtree();

            subnodes.Read();

            BasePageJob job = new BasePageJob(widgetName, newWidget, subnodes.ReadOuterXml());

            return job;
        }
        private BasePageJob AddTemplateJob(XmlReader reader)
        {
            BasePageJob job = new BasePage.BasePageJob(reader.GetAttribute("name"), Template.getTemplate(reader.GetAttribute("id")));

            while (reader.Read())
                if (reader.NodeType == XmlNodeType.Element)
                {
                    XmlReader subNodes = reader.ReadSubtree();

                    while (subNodes.Read())
                        if (subNodes.NodeType == XmlNodeType.Element)
                            switch (subNodes.Name)
                            {
                                case "template":
                                    job.AddJob(AddTemplateJob(subNodes));
                                    subNodes.Skip();
                                    break;
                                case "widget":
                                    job.AddJob(AddWidgetJob(subNodes));
                                    subNodes.Skip();
                                    break;
                            }
                }

            return job;
        }
        private static BasePage GenerateNewPage(String basePageID)
        {
            BasePage newPage = new BasePage(basePageID);
            _cachedBasePagesHash.Add(basePageID, newPage);
            _cachedBasePages.Add(newPage);
            newPage.InitializeBasePage();

            return newPage;
        }
        private static CSSSnippet GenerateNewCSSSnippet(String snippetID)
        {
            CSSSnippet newSnippet = new CSSSnippet(snippetID);
            newSnippet.InitializeCSSSnippet();

            _cachedCssSnippetsHash.Add(snippetID, newSnippet);
            _cachedCssSnippets.Add(newSnippet);

            return newSnippet;
        }

        // Public Classes
        [Serializable()]
        public class BasePageJob
        {
            // Private Variables
            private BasePageJobType _type;
            private List<BasePageJob> _jobs = new List<BasePageJob>();
            private String _name = "";
            private String _xmlSettings = "";

            // Internal Properties
            internal BasePageJobType Type
            {
                get { return _type; }
                set { _type = value; }
            }
            internal List<BasePageJob> Jobs
            { get { return _jobs; } }
            internal String Name
            { get { return _name; } }
            internal String XmlSettings
            { get { return _xmlSettings; } }

            // Internal Constructors
            internal BasePageJob(String name, BasePageJobType type)
            {
                _name = name;
                _type = type;
                _xmlSettings = "";
            }
            internal BasePageJob(String name, BasePageJobType type, String xmlSettings)
            {
                _name = name;
                _type = type;
                _xmlSettings = xmlSettings;
            }

            // Internal Methods
            internal void AddJob(BasePageJob job)
            { _jobs.Add(job); }
            internal StringBuilder Generate(HttpContext rawContext, BasePage.ContextManager context, BasePage.PageSettings pageSettings, Stopwatch timer, Boolean useDiagnostics)
            {
                StringBuilder body = new StringBuilder();

                body.Append(_type.GeneratePage(context, pageSettings, _xmlSettings, timer, useDiagnostics));

                foreach (BasePageJob job in _jobs)
                    if (job._name != null && job._name != "")
                        body.Replace("{" + job._name + "}", job.Generate(rawContext, context, pageSettings, timer, useDiagnostics).ToString());

                return body;
            }
            internal Boolean TestPostBack(String tempGuid, String postBackID, BasePage.ContextManager context, BasePage.PageSettings pageSettings, Stopwatch timer, Boolean useDiagnostics)
            {
                if (typeof(WidgetBase).IsAssignableFrom(_type.GetType()))
                    if (((WidgetBase)_type).ID == postBackID)
                    {
                        pageSettings.PostBackGuid = tempGuid;
                        ((WidgetBase)_type).AddPostBackID(tempGuid, ((WidgetBase)_type).PostBack(context, pageSettings, this.XmlSettings, timer, useDiagnostics).ToString());

                        return true;
                    }

                foreach (BasePageJob job in _jobs)
                    if (job.TestPostBack(tempGuid, postBackID, context, pageSettings, timer, useDiagnostics))
                        return true;

                return false;
            }

            [Serializable()]
            public abstract class BasePageJobType
            {
                abstract internal StringBuilder GeneratePage(BasePage.ContextManager context, BasePage.PageSettings pageSettings, String xmlSettings, Stopwatch timer, Boolean useDiagnostics);
            }

        }
        public class PageSettings
        {
            // Privare Variables
            private Boolean _headerChanges = false;
            private Dictionary<String, String> _replaceItems = new Dictionary<String, String>();
            private List<CSSSnippet> _cssSnippets = new List<CSSSnippet>();
            private String _basePageID;
            private String _postBackGuid = "";
            private StringBuilder _title = new StringBuilder();
            private StringBuilder _javaScript = new StringBuilder();
            private StringBuilder _metaKeywords = new StringBuilder();
            private StringBuilder _metaDescription = new StringBuilder();
            private StringBuilder _javaScriptDefered = new StringBuilder();

            // Public Properties
            public String Title
            { get { return _title.ToString(); } }
            public String BasePageID
            { get { return _basePageID; } }
            public String MetaKeywords
            { get { return _metaKeywords.ToString(); } }
            public String MetaDescription
            { get { return _metaDescription.ToString(); } }

            // Internal Properties
            internal Boolean HeaderChanges
            {
                get { return _headerChanges; }
                set { _headerChanges = value; }
            }
            internal Dictionary<String, String> ReplaceItems
            { get { return _replaceItems; } }
            internal List<CSSSnippet> CssSnippets
            { get { return _cssSnippets; } }
            internal String JavaScript
            { get { return _javaScript.ToString(); } }
            internal String PostBackGuid
            {
                get { return _postBackGuid; }
                set { _postBackGuid = value; }
            }
            internal String JavaScriptDefered
            { get { return _javaScriptDefered.ToString(); } }

            // Public Constructors
            public PageSettings(BasePage page)
            {
                _basePageID = page.ID;
            }

            // Internal Methods
            internal void AddCssSnippet(CSSSnippet snippet)
            {
                foreach (CSSSnippet item in _cssSnippets)
                    if (item.ID == snippet.ID)
                        return;

                _cssSnippets.Add(snippet);
            }
            internal void AddCssSnippet(CSSSnippet[] snippets)
            {
                foreach (CSSSnippet item in snippets)
                    this.AddCssSnippet(item);
            }
            internal void AddCssSnippet(List<CSSSnippet> snippets)
            {
                foreach (CSSSnippet item in snippets)
                    this.AddCssSnippet(item);
            }

            internal void AppendJavaScript(String javaScript)
            { _javaScript.Append(javaScript); }
            internal void AppendJavaScriptDefered(String javaScriptDefered)
            { _javaScriptDefered.Append(javaScriptDefered); }

            // Public Methods
            /// <summary>
            /// Will append the string to a stringbuilder object.
            /// This object will then be used to generate your page title. 
            /// </summary>
            /// <param name="title">The value to append</param>
            public void AppendTitle(String title)
            { _title.Append(title); }
            /// <summary>
            /// Will append the string to a stringbuilder object.
            /// This object will then be used to generate your page meta keywords.
            /// </summary>
            /// <param name="metaKeywords">The value to append</param>
            public void AppendMetaKeywords(String metaKeywords)
            { _metaKeywords.Append(metaKeywords); }
            /// <summary>
            /// Will append the string to a stringbuilder object.
            /// This object will then be used to generate your meta description.
            /// </summary>
            /// <param name="metaDescription">The value to append</param>
            public void AppendMetaDescription(String metaDescription)
            { _metaDescription.Append(metaDescription); }
            /// <summary>
            /// <para>
            /// Will add a key and value to  dictionary.
            /// This object will then be used to replace the the dictionary key value with the dictionary key item value.
            /// </para>
            /// <para>
            /// For example if you pass the string to replace as "[REPLACE]" and the string to replace with as "replace string".
            /// Then the outcome will be where ever "[REPLACE]" is in your templates it will become "replace string".
            /// </para>
            /// </summary>
            /// <param name="stringToReplace">The string to replace e.g "[REPLACE]"</param>
            /// <param name="StringToReplaceWith">The string to replace with e.g "replace string"</param>
            public void AddReplaceItem(String stringToReplace, String StringToReplaceWith)
            {
                _replaceItems.Add(stringToReplace, StringToReplaceWith);
            }

        }
        public class ContextManager
        {
            // Private Variables
            private Dictionary<String, Object> _sessionItems = new Dictionary<String, Object>();
            private HttpContext _context = null;
            private NameValueCollection _queryString = new NameValueCollection();
            private String _url = "";

            // Public Properties
            public Boolean IsSecureConnection
            {
                get { return _context.Request.IsSecureConnection; }
            }
            public HttpFileCollection Files
            {
                get { return _context.Request.Files; }
            }
            public String SessionID
            {
                get { return _context.Session.SessionID; }
            }
            public String URL
            {
                get
                {
                    if (_context != null)
                        return _context.Request.Url.ToString();
                    else
                        return _url;
                }
            }

            // Public Constructors
            public ContextManager(HttpContext context)
            {
                _context = context;
            }
            public ContextManager(String url, NameValueCollection queryString)
            {
                _url = url;
                _queryString = queryString;
            }

            // Public Methods

            /// <summary>
            /// Redirects user to the specified URL
            /// </summary>
            /// <param name="url">The target location</param>
            public void Redirect(String url)
            {
                Redirect(url, true);
            }
            public void Redirect(String url, Boolean endResponse)
            {
                _context.Response.Redirect(url, endResponse);
            }
            /// <summary>
            /// Gets the value of an item in the collections: HttpRequest.Form, HttpRequest.QueryString, HttpRequest.ServerVariables
            /// </summary>
            /// <param name="name">The name of the key to retrieve the value from</param>
            /// <returns>The value of the key</returns>
            public String Request(String name)
            {
                if (_context != null)
                    if (_context.Request[name] != null)
                        return _context.Request[name];

                return "";
            }
            /// <summary>
            /// Gets the full QueryString
            /// </summary>
            /// <returns>The QueryString</returns>
            public String GetFullQueryString()
            {
                if (_context != null)
                    if (_context.Request.QueryString.ToString() != null)
                        return _context.Request.QueryString.ToString();

                return "";
            }
            /// <summary>
            /// Gets an Object from the HttpContext.Session
            /// </summary>
            /// <param name="name">The name of the value to be retrieved</param>
            /// <returns>The Object of the Session</returns>
            public Object GetSession(String name)
            {
                if (_context != null)
                    if (_context.Session[name] != null)
                        return _context.Session[name];
                    else
                        return "";
                else
                    return _sessionItems[name];
            }
            /// <summary>
            /// Gets the form value with a specified name.
            /// </summary>
            /// <param name="name">The name of the value to be retrieved</param>
            /// <returns>The value of form variable</returns>
            public String GetFormValue(String name)
            {
                if (_context != null)
                    if (_context.Request.Form[name] != null)
                        return _context.Request.Form[name];

                return "";
            }
            /// <summary>
            /// Gets an HttpCookie from the HttpRequest.Cookies
            /// </summary>
            /// <param name="name">The name of the value to be retrieved</param>
            /// <returns>HttpCookie</returns>
            public HttpCookie GetCookie(String name)
            {
                if (_context != null)
                    return _context.Request.Cookies[name];

                return new HttpCookie(name, "");
            }
            /// <summary>
            /// Sets a HttpCookie
            /// </summary>
            /// <param name="cookie">The HttpCookie to be added to teh HttpCookieCollection</param>
            public void SetCookie(HttpCookie cookie)
            {
                if (_context.Response.Cookies[cookie.Name] != null)
                    _context.Response.Cookies.Remove(cookie.Name);

                _context.Response.Cookies.Add(cookie);

            }
            /// <summary>
            /// Gets a value from the collection of ServerVariables
            /// </summary>
            /// <param name="name">The name of the value to be retrieved</param>
            /// <returns>The value of the key retrieved</returns>
            public String ServerVariables(String name)
            {
                if (_context.Request.ServerVariables[name] != null)
                    return _context.Request.ServerVariables[name].ToString();
                else
                    return "";
            }
            /// <summary>
            /// URL encodes a String
            /// </summary>
            /// <param name="urlToEncode">The string to encode</param>
            /// <returns>The URL encoded string</returns>
            public String UrlEncode(String urlToEncode)
            {
                if (_context != null)
                    return _context.Server.UrlEncode(urlToEncode);
                else
                    return System.Web.HttpUtility.UrlEncode(urlToEncode);
            }
            /// <summary>
            /// URL decodes a String
            /// </summary>
            /// <param name="urlToDecode">The string to decode</param>
            /// <returns>The URL decoded string</returns>
            public String UrlDecode(String urlToDecode)
            {
                if (_context != null)
                    return _context.Server.UrlDecode(urlToDecode);
                else
                    return System.Web.HttpUtility.UrlEncode(urlToDecode);
            }
            /// <summary>
            /// Html encodes a String
            /// </summary>
            /// <param name="htmlToEncode">The string to encode</param>
            /// <returns>The URL encoded string</returns>
            public String HtmlEncode(String htmlToEncode)
            {
                if (_context != null)
                    return _context.Server.HtmlEncode(htmlToEncode);
                else
                    return System.Web.HttpUtility.UrlEncode(htmlToEncode);
            }
            /// <summary>
            /// Html decodes a String
            /// </summary>
            /// <param name="htmlToDecode">The string to decode</param>
            /// <returns>The URL decoded string</returns>
            public String HtmlDecode(String htmlToDecode)
            {
                if (_context != null)
                    return _context.Server.HtmlDecode(htmlToDecode);
                else
                    return System.Web.HttpUtility.UrlEncode(htmlToDecode);
            }
            /// <summary>
            /// Gets the value of a key in the HttpRequest.QueryString collection
            /// </summary>
            /// <param name="name">The name of the value to be retrieved</param>
            /// <returns>The value of the key retrieved</returns>
            public String GetQueryStringValue(String name)
            {
                if (_context != null)
                    if (_context.Request.QueryString[name] != null)
                        return _context.Request.QueryString[name];
                    else
                        return "";
                else
                    return _queryString[name];
            }
            /// <summary>
            /// Sets an Object in the HttpContext.Session Collection
            /// </summary>
            /// <param name="name">The name of the session to set</param>
            /// <param name="value">The value of the session to be set</param>
            public void SetSession(String name, Object value)
            {
                if (_context != null)
                    _context.Session[name] = value;
                else
                    _sessionItems[name] = value;
            }
        }

        // Internal Classes
        [Serializable()]
        internal class CSSSnippet
        {
            // Privare Variables
            private Boolean _runThreadAgain = true;
            private CssType _type;
            private DateTime _dateChanged;
            private Int32 _minRefreshTime = 60000;
            private Int32 _maxRefreshTime = 120000;
            private String _id;
            private String _body;
            private String _version = "0";

            // Public Enums
            public enum CssType
            {
                none,
                all,
                braille,
                embossed,
                handheld,
                print,
                projection,
                screen,
                screen_IE6,
                screen_lt_IE6,
                screen_gt_IE6,
                screen_IE7,
                screen_lt_IE7,
                screen_gt_IE7,
                screen_IE8,
                screen_lt_IE8,
                screen_gt_IE8,
                screen_IE9,
                screen_lt_IE9,
                screen_gt_IE9,
                speech,
                tty,
                tv
            }

            // Public Properties
            public CssType Type
            { get { return _type; } }
            public DateTime DateChanged
            { get { return _dateChanged; } }
            public String ID
            { get { return _id; } }
            public String Body
            { get { return _body; } }
            public String Version
            { get { return _version; } }

            // Private Delegates
            private delegate void loadCSSSnippetDelegate(Int32 startStall);

            // Public Constructors
            public CSSSnippet(String id)
            {
                _id = id;
            }

            // Internal Methods
            internal void InitializeCSSSnippet()
            {
                _runThreadAgain = false;
                loadCSSSnippet(0);
                _runThreadAgain = true;
                AutoLoad();
            }

            // Private Methods
            private void AutoLoad()
            {
                loadCSSSnippetDelegate autoLoad = new loadCSSSnippetDelegate(loadCSSSnippet);
                autoLoad.BeginInvoke(StaticObjects.RandomFactory.Create().Next(_minRefreshTime, _maxRefreshTime), new AsyncCallback(AutoLoadEnd), null);
            }
            private void AutoLoadEnd(IAsyncResult result)
            {
            }

            // Public Methods
            public void StopAutoLoad()
            {
                _runThreadAgain = false;
            }
            public void BeginAutoLoad()
            {
                _runThreadAgain = true;
                AutoLoad();
            }
            public void loadCSSSnippet(Int32 startStall)
            {
                DataRow dr;
                DataSet ds;
                String sql;

                if (startStall != 0)
                    Thread.Sleep(startStall);

                // Load the BasePage
                sql = "select * from getCMSCssSnippet('" + this.ID + "') Order by Version Desc";
                ds = StaticObjects.GetDataset(sql);

                if (ds.Tables.Count == 0 || ds.Tables[0].Rows.Count == 0)
                    throw new OBiHTTPHandler.CMSException("CMS Snippet Cannot Load.", new Dictionary<String, String>() {
                                                                                 {"CMS Snippet ID", this.ID} });

                dr = ds.Tables[0].Rows[0];

                // Has the version changed, if so reload the pages settings
                if (_version != dr["Version"].ToString())
                {
                    _body = dr["Body"].ToString();
                    _type = (CssType)Enum.Parse(typeof(CssType), dr["Type"].ToString());
                    _version = dr["Version"].ToString();

                    // Update the changes time to now.
                    _dateChanged = DateTime.UtcNow;
                }

                // Automaticly run again?
                if (_runThreadAgain)
                    AutoLoad();

            }
            public static String CssTypeDeclaration(CssType type, String BasePageID)
            {
                switch (type)
                {
                    case CssType.all:
                        return "<link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=all\" media=\"all\" rel=\"Stylesheet\" type=\"text/css\"/>";
                    case CssType.braille:
                        return "<link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=braille\" media=\"braille\" rel=\"Stylesheet\" type=\"text/css\"/>";
                    case CssType.embossed:
                        return "<link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=embossed\" media=\"embossed\" rel=\"Stylesheet\" type=\"text/css\"/>";
                    case CssType.handheld:
                        return "<link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=handheld\" media=\"handheld\" rel=\"Stylesheet\" type=\"text/css\"/>";
                    case CssType.print:
                        return "<link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=print\" media=\"print\" rel=\"Stylesheet\" type=\"text/css\"/>";
                    case CssType.projection:
                        return "<link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=projection\" media=\"projection\" rel=\"Stylesheet\" type=\"text/css\"/>";
                    case CssType.screen:
                        return "<link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/>";
                    case CssType.screen_IE6:
                        return "<!--[if IE 6]><link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen_IE6\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/><![endif]-->";
                    case CssType.screen_lt_IE6:
                        return "<!--[if lt IE 6]><link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen_lt_IE6\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/><![endif]-->"; ;
                    case CssType.screen_gt_IE6:
                        return "<!--[if gt IE 6]><link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen_gt_IE6\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/><![endif]-->"; ;
                    case CssType.screen_IE7:
                        return "<!--[if IE 7]><link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen_IE7\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/><![endif]-->"; ;
                    case CssType.screen_lt_IE7:
                        return "<!--[if lt IE 7]><link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen_lt_IE7\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/><![endif]-->"; ;
                    case CssType.screen_gt_IE7:
                        return "<!--[if gt IE 7]><link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen_gt_IE7\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/><![endif]-->"; ;
                    case CssType.screen_IE8:
                        return "<!--[if IE 8]><link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen_IE8\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/><![endif]-->"; ;
                    case CssType.screen_lt_IE8:
                        return "<!--[if lt IE 8]><link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen_lt_IE8\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/><![endif]-->"; ;
                    case CssType.screen_gt_IE8:
                        return "<!--[if gt IE 8]><link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen_gt_IE8\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/><![endif]-->"; ;
                    case CssType.screen_IE9:
                        return "<!--[if IE 9]><link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen_IE9\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/><![endif]-->"; ;
                    case CssType.screen_lt_IE9:
                        return "<!--[if lt IE 9]><link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen_lt_IE9\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/><![endif]-->"; ;
                    case CssType.screen_gt_IE9:
                        return "<!--[if gt IE 9]><link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=screen_gt_IE9\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\"/><![endif]-->"; ;
                    case CssType.speech:
                        return "<link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=speech\" media=\"speech\" rel=\"Stylesheet\" type=\"text/css\"/>";
                    case CssType.tty:
                        return "<link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=tty\" media=\"tty\" rel=\"Stylesheet\" type=\"text/css\"/>";
                    case CssType.tv:
                        return "<link href=\"/" + BasePageID + ".css?OBiVersion=Dynamic&amp;Type=tv\" media=\"tv\" rel=\"Stylesheet\" type=\"text/css\"/>";
                    default:
                        return "";
                }
            }

        }

        // Internal Structures
        internal struct PageContent
        {
            // Private Variables
            private Boolean _online;
            private DateTime _lastChanged;
            private String _body;

            // Public Properties
            public Boolean Online
            { get { return _online; } }
            public DateTime LastChanged
            { get { return _lastChanged; } }
            public String Body
            { get { return _body; } }

            // Public Constructors
            public PageContent(String body, DateTime lastChanged, Boolean online)
            {
                _body = body;
                _lastChanged = lastChanged;
                _online = online;
            }

        }
    }
}
